<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title> new document </title>
<meta name="author" content="" />
<meta name="keywords" content="" />
<meta name="description" content="" />
<style>
body{margin:0;padding:10px}
#test{ width:200px;margin:0;padding:0;height:20px;}
#zl-outer{position:absolute;z-index:999;left:10px;top:34px;width:202px;border:solid 1px #ccc;display:none}
#zl{ margin:0;padding:0;list-style:none}
#zl li{cursor:pointer;height:20px;line-height:20px;white-space:nowrap;width:100%;overflow:hidden;text-overflow:ellipsis;-o-text-overflow:ellipsis}
#zl li.curr{ background:#ccc;margin:0;padding:0;display:block;width:100%}
#log{min-height:20px; overflow:auto; border:solid 1px red;margin-left:250px;}

</style>
</head>

<body>
<form name="autocomplete" action="">
<input type="text" name="test" id="test" />
<input type="submit" value="提交" name="submit" />
<input type="button" value="clear" autocomplete="off" name="clear" id="clear" /><!---->
</form>
<div id="zl-outer">
<ul id="zl">
<!--
	<li name="0" value="hello"><b>0</b>-hello</li>
	<li name="1" value="world"><b>1</b>-world</li>
	<li name="2" value="are y"><b>2</b>-are you ok</li>
	<li name="3" value="alone"><b>3</b>-alone</li>
	<li name="4" value="lonel"><b>4</b>-lonely</li>
	<li name="5" value="faceb"><b>5</b>-facebook</li>
	<li name="6" value="twitt"><b>6</b>-twitter hello world, how are you , are you ok</li>
	<li name="7" value="youtu"><b>7</b>-youtube</li>
	<li name="8" value="ookon"><b>8</b>-ookong</li>
	<li name="9" value="yogob"><b>9</b>-yogoba</li>
-->
</ul>         
</div>
<div id="log"></div>
<script>//<![CDATA[
function $( id){
	var ele = document.getElementById( id);

	ele.bind = function( type, func){
		if( ele.addEventListener){
			ele.addEventListener( type, func, false)
		}else if( ele.attachEvent){
			ele.attachEvent( 'on' + type, func);
		}else{
			ele[ 'on' + type ] = func;
		}

		return ele;
	}

	return ele;
}
$.trim = function( msg){
	return ( msg || '').replace( /^\s*|\s*$/g, '');
}
function Cache(){
	this.cache = {};
}
Cache.prototype.set = function( key, value, isObject){
	var forbiddenKeys = [ 'has', 'set', 'get', 'remove']; // in case of override
	for( var i = 0, len = forbinddenKeys.length; i < len; i++ ){
		if( key == forbinddenKeys[ i]){
			throw new Error( ' key "' + key + '" is forbidden');
			return;
		}
	}
	if( isObject ){
		this.cache[ key ] = JSON.stringify( value);
	}else{
		this.cache[ key ] = value;
	}
}
Cache.prototype.get = function( key, isObject ){
	return this.cache[ key] ? 
		( isObject ?  JSON.parse( this.cache[ key]) : this.cache[ key] ) :
		null;
}
Cache.prototype.has = function( key){
	return this.cache.hasOwnPropery( key ) ? true : false;
}
Cache.prototype.remove = function( keys){
	for( var key in keys){
		if( this.cache.hasOwnProperty( key) ){
			delete this.cache[ key]
		}
	}
}
function log( m){ $( 'log').innerHTML += m + '<br />' }
function cl( msg){
	//console.log( msg)
	$( 'log').innerHTML += msg + '<br />';
}
(function(){
var index = -1;
var lists = [];
var cache = new Cache;
var last_keywords = '';
var ele_count;

var getForm = function( input_ele){
	var ele = input_ele.parentNode; // TODO: test parentNode property
	while( ele.tagName !== 'FORM'){
		ele = ele.parentNode;
	}	
	if( ele.tagName  === 'FORM'){
		return ele;
	}else{
		return null;
	}
}
var getSubmitButton = function( form_ele){
	var input_eles = form_ele.getElementsByTagName( 'input');
	var i, len, type;
	for( i = 0, len = input_eles.length; i < len; i++){
		type = input_eles[ i].getAttribute( 'type');
		if( type === 'submit'){
			return input_eles[ i];
		}
	}
	return null;
}
var submitForm = function( input_ele ){
	var form_ele = getForm( input_ele);
	var submit_btn;
	if( form_ele){
		try{
			form_ele.submit();
		}catch( e){ // incase the input[ type="submit"] named "submit"
			submit_btn = getSubmitButton( form_ele);
			if( submit_btn ){
				submit_btn.click();
			}	
		}
	}
};
var setInputValue = function( v){
	$( 'test').value = v;
	//$( 'test').blur();
	//$( 'test').focus();
}
var init = function(){
	$( 'test').setAttribute( 'autocomplete', 'off');
};
var ajax2getList = function( keywords){
	var xhr = new XMLHttpRequest();
	xhr.onreadystatechange = function(){
		if( xhr.readyState == 4 ){
			if( xhr.status == 200){
				renderLists( xhr.responseText);
			}else{
				alert( 'ajax to get list error');
			}
		}
	}
	xhr.open( 'GET', 'ajax.php?q=' + encodeURIComponent( keywords) );
	xhr.send( null);

};
var renderLists = function( results){ // cache result
	cl( results);
	var json = JSON.parse( results);
	var q = json[ 0];
	var ul = $( 'zl');
	var li, innerHtml;
	ul.innerHTML = '';
	for( var i = 0, len = json[ 1].length; i < len; i++){
		li = document.createElement( 'li');
		li.setAttribute( 'name', i);
		li.setAttribute( 'value', json[ 1][ i] );
		innerHtml = json[ 1][ i].replace( q, '<b>' + q + '</b>');
		li.innerHTML = innerHtml;
		lists[ i ] = li; // fill the lists
		ul.appendChild( li);
	}
	ele_count = len;
};
$( 'test').bind( 'keyup', function(){
	var KEY = {};
   	KEY.carriage    = 13;
	KEY.upArrow     = 38;
	KEY.downArrow   = 40;
	KEY.esc  	    = 27;
	//var ele_count;

	var resetList = function(){
		if( index !== -1 ){
			lists[ index].removeAttribute( 'class');
		}
		$( 'zl-outer').style.display = 'none';
		index = -1;
		loadUserValue();
	};
	var checkInput = function(){
		var keywords = $.trim( $( 'test').value );
		if( keywords.length < 3 ){
			$( 'zl-outer').style.display = 'none';
			index = -1;
			return false;
		}
		$( 'zl-outer').style.display = 'block';
		return true;
	};
	var loadUserValue = function(){
		$( 'test').value = last_keywords;
	};
	var saveUserValue = function(){
		if( last_keywords == $.trim( $( 'test').value ) ){
			return;
		}
		last_keywords = $.trim( $( 'test').value );
		// TODO: check cache if it is there
		ajax2getList( last_keywords);
	}

	//ele_count = lists.length;
	return function( event){
		switch( event.keyCode){
			case KEY.downArrow:
				if( ! checkInput() ){
					return
				}
				if( index !== -1){ // index == -1: begin
					lists[ index].removeAttribute( 'class');
				}
				index++;
				if( index !== ele_count ){
					lists[ index ].setAttribute( 'class', 'curr');
					setInputValue( lists[ index].getAttribute( 'value'));
				}else{ // not exist
					index = -1;
					loadUserValue();
				}
				cl( index);
				break;
			case KEY.upArrow:
				if( ! checkInput() ){
					return
				}
				if( index !== -1 ){
					lists[ index ].removeAttribute( 'class');
				}
				index--;
				if( index === -2){
					index = ele_count - 1;
				}
				if( index !== -1){
					lists[ index ].setAttribute( 'class', 'curr');
					setInputValue( lists[ index].getAttribute( 'value'));
				}else{
					loadUserValue();
				}
				cl( index);
				break;
			case KEY.carriage:
				if( index !== -1){
					$( 'test').value = lists[ index].getAttribute( 'value');
					// REMIND: submit automatically
				}
				break;
			case KEY.esc:
				resetList();
				break;
			default:
				if( ! checkInput() ){
					return;
				}
				saveUserValue();
		}
	}
}() );
$( 'zl').bind( 'click', function( event){
	var srcEle = event.target || event.srcElement;		
	var keywords = srcEle.getAttribute( 'value');
	index = srcEle.getAttribute( 'name');
	$( 'test').value = keywords;	
	// TODO: submit the form
	submitForm( $( 'test') );
});
$( 'zl').bind( 'mouseover', function( event){
	var srcEle = event.target || event.srcElement;
	if( srcEle.tagName === 'B'){
		srcEle = srcEle.parentNode;
	}
	if( index !== -1){
		lists[ index].removeAttribute( 'class');
	}
	//cl( srcEle.tagName);
	index = srcEle.getAttribute( 'name');
	lists[ index].setAttribute( 'class', 'curr');
	//cl( index);
	//cl( Math.random() );
});
$( 'zl').bind( 'mouseout', function( event){
	var srcEle = event.target || event.srcElement;
	if( srcEle.tagName === 'B'){
		srcEle = srcEle.parentNode;
	}
	//cl( srcEle.tagName);
	index = srcEle.getAttribute( 'name');
	lists[ index].setAttribute( 'class', 'curr');
})
$( 'test').focus(); // TODO: fix unfocus
$( 'test').bind( 'blur', function(){
	//$( 'zl').style.display = 'none';
});

init();
})();

/* */
$( 'clear').bind( 'click', function(){
	$( 'log').innerHTML = '';
});
//BUG: firefox autocomplete = "off" does not work
//]]>
</script>
</body>
</html>
